class WebVoiceIntercomService {
	constructor() {
		this.audioContext = null;
		this.mediaStream = null;
		this.connections = new Map();
		this.workletNode = null;
		this.sequenceNumber = Math.floor(Math.random() * 65536);
		this.shouldReconnect = true;
		this.isShouting = false;
		this.audioProcessorUrl = null;
	}

	/**
	 * 建立WebSocket连接
	 * @param {string} streamUrl - 从getAudioChannel获取的流地址
	 * @returns {Promise<string>} 连接ID
	 */
	async startWebSocketSession(streamUrl) {
		return new Promise((resolve, reject) => {

			const url = new URL(streamUrl);
			const token = url.pathname.split('/').pop();
			const ssrc = parseInt(token) || 0;

			const socket = new WebSocket(streamUrl);
			socket.binaryType = 'arraybuffer';

			const connectionId = Date.now().toString();
			const connection = {
				socket,
				ssrc,
				lastActivity: Date.now(),
				isActive: false
			};

			const connectTimeout = setTimeout(() => {
				reject(new Error('Connection timeout'));
			}, 5000);

			socket.onopen = () => {
				clearTimeout(connectTimeout);
				console.log('WebSocket connected');

				connection.keepAliveInterval = setInterval(() => {
					this._sendKeepAlive(connection);
				}, 3000);

				connection.activityCheck = setInterval(() => {
					const inactiveTime = Date.now() - connection.lastActivity;
					if (inactiveTime > 10000) {
						console.warn(`Inactive for ${inactiveTime}ms, closing`);
						socket.close(1008, 'Inactivity timeout');
					}
				}, 1000);

				this.connections.set(connectionId, connection);
				resolve(connectionId);
			};

			socket.onerror = (error) => {
				clearTimeout(connectTimeout);
				console.error('WebSocket error:', error);
				reject(new Error('Connection failed'));
			};

			socket.onclose = (event) => {
				console.log('Connection closed:', {
					code: event.code,
					reason: event.reason,
					wasClean: event.wasClean
				});

				if (this.connections.has(connectionId)) {
					const conn = this.connections.get(connectionId);
					clearInterval(conn.keepAliveInterval);
					clearInterval(conn.activityCheck);
					this.connections.delete(connectionId);
				}

				if (!event.wasClean && this.shouldReconnect) {
					setTimeout(() => {
						console.log('Attempting reconnect...');
						this.startWebSocketSession(streamUrl)
							.catch(console.error);
					}, 2000);
				}
			};
		});
	}

	/**
	 * 开始采集和传输音频
	 */
	async startAudioCapture() {
		try {
			if (!this._isRecordingSupported()) {
				throw new Error('Recording not supported');
			}

			if (!this._isSecureContext()) {
				throw new Error('HTTPS or localhost required');
			}

			this.mediaStream = await this._getUserMediaWithFallback();
			await this._initAudioWorklet();

		} catch (error) {
			this._handleRecorderError(error);
			throw this._translateError(error);
		}
	}

	/**
	 * 设置喊话状态
	 * @param {boolean} isShouting - 是否正在喊话
	 */
	setShouting(isShouting) {
		this.isShouting = isShouting;
		if (this.workletNode) {
			this.workletNode.port.postMessage({
				type: 'setShouting',
				isShouting
			});
		}
	}

	/**
	 * 停止对讲并释放资源
	 * @param {string} connectionId - 连接ID
	 */
	stopVoiceIntercom(connectionId) {
		if (!this.connections.has(connectionId)) return;

		const conn = this.connections.get(connectionId);

		// 1. 立即禁止重连
		this.shouldReconnect = false;

		// 2. 清除所有定时器（使用更彻底的清除方式）
		if (conn.keepAliveInterval) {
			clearInterval(conn.keepAliveInterval);
			conn.keepAliveInterval = null; // 防止内存泄漏
		}
		if (conn.activityCheck) {
			clearInterval(conn.activityCheck);
			conn.activityCheck = null;
		}

		// 3. 移除所有事件监听器（防止残留回调）
		conn.socket.onopen = null;
		conn.socket.onerror = null;
		conn.socket.onmessage = null;
		conn.socket.onclose = null;

		// 4. 强制关闭 WebSocket（不再等待优雅关闭）
		if (conn.socket.readyState === WebSocket.OPEN ||
			conn.socket.readyState === WebSocket.CONNECTING) {
			try {
				conn.socket.close(1000, 'User requested close');
			} catch (e) {
				console.error('Error closing WebSocket:', e);
				// 如果标准 close 失败，尝试强制终止
				if (typeof conn.socket.terminate === 'function') {
					conn.socket.terminate();
				} else {
					// 如果浏览器环境没有 terminate()，可以手动丢弃引用
					conn.socket = null;
				}
			}
		}

		// 5. 从连接池删除
		this.connections.delete(connectionId);
		console.log('WebSocket connection closed', {
			connectionId
		});

		// 6. 停止音频采集
		this.stopAudioCapture();
	}

	// ===== 私有方法 ===== //

	async _initAudioWorklet() {
		this.audioContext = new(window.AudioContext || window.webkitAudioContext)({
			sampleRate: 8000
		});

		if (this.audioContext.state === 'suspended') {
			await this.audioContext.resume();
		}

		// 注册AudioWorkletProcessor
		if (!this.audioProcessorUrl) {
			const processorCode = this._getAudioProcessorCode();
			this.audioProcessorUrl = URL.createObjectURL(
				new Blob([processorCode], {
					type: 'application/javascript'
				})
			);
		}

		try {
			await this.audioContext.audioWorklet.addModule(this.audioProcessorUrl);
		} catch (error) {
			console.error('Failed to load audio processor:', error);
			throw new Error('Audio processing initialization failed');
		}

		const source = this.audioContext.createMediaStreamSource(this.mediaStream);
		this.workletNode = new AudioWorkletNode(this.audioContext, 'white-noise-processor');

		// 设置token给processor
		const activeConnection = Array.from(this.connections.values())[0];
		if (activeConnection) {
			this.workletNode.port.postMessage({
				type: 'setToken',
				token: activeConnection.ssrc
			});
		}

		// 设置喊话状态
		this.workletNode.port.postMessage({
			type: 'setShouting',
			isShouting: this.isShouting
		});

		// 接收处理后的音频数据
		this.workletNode.port.onmessage = (event) => {
			if (event.data.type === 'rtpPacket') {
				this._sendRtpPacketToAllConnections(event.data.data);
			}
		};

		source.connect(this.workletNode);
		this.workletNode.connect(this.audioContext.destination);
	}

	_sendRtpPacketToAllConnections(packet) {
		this.connections.forEach(conn => {
			if (conn.socket.readyState === WebSocket.OPEN) {
				conn.socket.send(packet);
				conn.lastActivity = Date.now();
			}
		});
	}

	_sendKeepAlive(connection) {
		// 如果连接已标记为关闭，不再发送
		if (connection.socket.readyState !== WebSocket.OPEN) return;

		const payload = new TextEncoder().encode("AudioKeepAlive");
		if (payload.length !== 14) {
			throw new Error(`保活包长度必须为14字节，实际为${payload.length}`);
		}

		const packet = this._createRtpPacket(payload, connection.ssrc);
		connection.socket.send(packet);
		connection.lastActivity = Date.now();
	}

	_createRtpPacket(payload, ssrc = 0) {
		const header = new ArrayBuffer(12);
		const view = new DataView(header);

		view.setUint8(0, 0x80);
		view.setUint8(1, 8);
		view.setUint16(2, this.sequenceNumber++);
		view.setUint32(4, Math.floor(Date.now() / 1000));
		view.setUint32(8, ssrc);

		const packet = new Uint8Array(12 + payload.length);
		packet.set(new Uint8Array(header), 0);
		packet.set(payload, 12);

		return packet.buffer;
	}

	_getAudioProcessorCode() {
		return `
      const QUANT_MASK = 0x0f;
      const SEG_SHIFT = 0x04;
      const seg_end = new Uint16Array([
        0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF
      ]);

      function search(val, table, size) {
        for (let i = 0; i < size; i++) {
          if (val <= table[i]) return i;
        }
        return size;
      }

      function linear2alaw(pcm_val) {
        let mask;
        let seg;
        let aval;

        if (pcm_val >= 0) {
          mask = 0xD5;
        } else {
          mask = 0x55;
          pcm_val = -pcm_val - 8;
        }

        seg = search(pcm_val, seg_end, 8);

        if (seg >= 8) {
          return 0x7F ^ mask;
        } else {
          aval = seg << SEG_SHIFT;
          if (seg < 2) {
            aval |= (pcm_val >> 4) & QUANT_MASK;
          } else {
            aval |= (pcm_val >> (seg + 3)) & QUANT_MASK;
          }
          return aval ^ mask;
        }
      }

      function alawFromPCM(data) {
        let dest = new Uint8Array(data.length);
        for (let i = 0; i < data.length; i++) {
          dest[i] = linear2alaw(data[i]);
        }
        return dest;
      }

      class WhiteNoiseProcessor extends AudioWorkletProcessor {
        constructor() {
          super();
          this.frameSize = 320;
          this.buffer = new Int16Array(0);
          this.energyThreshold = 0.01;
          this.isShouting = false;
          this.token = 0;

          this.port.onmessage = (event) => {
            if (event.data.type === 'setToken') {
              this.token = event.data.token;
            }
            if (event.data.type === 'setShouting') {
              this.isShouting = event.data.isShouting;
            }
          };
        }

        calculateRMS(inputData) {
          let sum = 0;
          for (let i = 0; i < inputData.length; i++) {
            sum += inputData[i] * inputData[i];
          }
          const mean = sum / inputData.length;
          return Math.sqrt(mean);
        }

        process(inputs, outputs, parameters) {
          if (!this.isShouting) return true;

          const input = inputs[0];
          const inputData = input[0];
          if (!inputData) return true;

          const rms = this.calculateRMS(inputData);
          if (rms < this.energyThreshold) {
            this.buffer = new Int16Array(0);
            return true;
          }

          const pcmData = new Int16Array(inputData.length);
          for (let i = 0; i < inputData.length; i++) {
            pcmData[i] = Math.max(-32768, Math.min(32767, inputData[i] * 32768));
          }

          const newBuffer = new Int16Array(this.buffer.length + pcmData.length);
          newBuffer.set(this.buffer);
          newBuffer.set(pcmData, this.buffer.length);
          this.buffer = newBuffer;

          while (this.buffer.length >= this.frameSize) {
            const frame = this.buffer.subarray(0, this.frameSize);
            this.buffer = this.buffer.subarray(this.frameSize);
            
            const g711aData = alawFromPCM(frame);
            const rtpPacket = this.createRTPPacket(g711aData);
            
            this.port.postMessage({ 
              type: 'rtpPacket', 
              data: rtpPacket,
              frameLength: g711aData.length 
            });
          }

          return true;
        }

        createRTPPacket(data) {
          const payloadType = 8;
          const sequenceNumber = Math.floor(Math.random() * 65535);
          const timestamp = Date.now();
          const ssrc = this.token;

          const header = new Uint8Array(12);
          header[0] = 0x80;
          header[1] = payloadType;
          header[2] = (sequenceNumber >> 8) & 0xFF;
          header[3] = sequenceNumber & 0xFF;
          header[4] = (timestamp >> 24) & 0xFF;
          header[5] = (timestamp >> 16) & 0xFF;
          header[6] = (timestamp >> 8) & 0xFF;
          header[7] = timestamp & 0xFF;
          header[8] = (ssrc >> 24) & 0xFF;
          header[9] = (ssrc >> 16) & 0xFF;
          header[10] = (ssrc >> 8) & 0xFF;
          header[11] = ssrc & 0xFF;

          const rtpPacket = new Uint8Array(header.length + data.length);
          rtpPacket.set(header, 0);
          rtpPacket.set(data, header.length);

          return rtpPacket.buffer;
        }
      }

      registerProcessor("white-noise-processor", WhiteNoiseProcessor);
    `;
	}

	// ===== 工具方法 ===== //

	_isRecordingSupported() {
		return !!(
			navigator.mediaDevices &&
			navigator.mediaDevices.getUserMedia &&
			(window.AudioContext || window.webkitAudioContext)
		);
	}

	_isSecureContext() {
		return (
			window.isSecureContext ||
			location.protocol === 'https:' || ['localhost', '127.0.0.1'].includes(location.hostname)
		);
	}

	async _getUserMediaWithFallback() {
		try {
			return await navigator.mediaDevices.getUserMedia({
				audio: {
					sampleRate: 8000,
					channelCount: 1,
					echoCancellation: true,
					noiseSuppression: true,
					autoGainControl: false
				},
				video: false
			});
		} catch (error) {
			if (error.name === 'OverconstrainedError') {
				return await navigator.mediaDevices.getUserMedia({
					audio: true
				});
			}
			throw error;
		}
	}

	_translateError(error) {
		const errorMap = {
			'NotFoundError': '未找到麦克风',
			'NotAllowedError': '麦克风访问被拒绝',
			'NotReadableError': '麦克风正在使用中',
			'OverconstrainedError': '无法满足音频要求',
			'SecurityError': '需要HTTPS或localhost'
		};
		return new Error(errorMap[error.name] || error.message);
	}

	_handleRecorderError(error) {
		console.error('Recorder error:', error);
		if (this.mediaStream) {
			this.mediaStream.getTracks().forEach(track => track.stop());
			this.mediaStream = null;
		}
		if (this.workletNode) {
			this.workletNode.disconnect();
			this.workletNode = null;
		}
		if (this.audioContext) {
			this.audioContext.close().catch(console.error);
			this.audioContext = null;
		}
	}

	stopAudioCapture() {
		this._handleRecorderError(new Error('User stopped'));
	}
}

// 导出单例实例
const voiceIntercomService = new WebVoiceIntercomService();
export default voiceIntercomService;